#include "parseMessage.h"
//#include "mysqlDBConnPool.h"
#include <string>

/*//////////////////////////////////////////
  解析http协议
//////////////////////////////////////////*/

#define WEBROOT "wwwroot" //资源根目录
#define HOMEPAGE "index.html" //首页资源
#define HTTPVERSION "HTTP/1.1"

std::string code2Description(size_t code)
{
  switch(code)
  {
  case 200:
    return "ok";
    break;
  case 400:
    return "not found";
    break;
  default:
    return "not found";
    break;
  }
}

std::string suffix2Desc(std::string& suffix)
{
  //Logger::info("---suffix suffix:%s", suffix.c_str());
  if(strcasecmp(suffix.c_str(), "html") == 0)
  {
    return "text/html";
  }
  else if(strcasecmp(suffix.c_str(), "css") == 0)
  {
    return "text/css";
  }
  else if(strcasecmp(suffix.c_str(), "js") == 0)
  {
    return "application/javascript";
  }
  else if(strcasecmp(suffix.c_str(), "jpg") == 0)
  {
    return "image/jpeg";
  }
  else if(strcasecmp(suffix.c_str(), "png") == 0)
  {
    return "image/png";
  }
  else if(strcasecmp(suffix.c_str(), "gif") == 0)
  {
    return "image/gif";
  }
  else if(strcasecmp(suffix.c_str(), "ico") == 0)
  {
    return "image/x-icon";
  }
  else 
  {
    return "text/html";
  }
}

HttpEndPoint::HttpEndPoint(int sock)
  :sock_(sock)
{
   
}

HttpEndPoint::~HttpEndPoint()
{
  //Logger::info("~HttpEndPoint sock:%d", sock_);
  //HttpMessageQueue* hq = HttpMessageQueue::getInstance();
  //if(hq)
  //{
  //  hq->subEndPointNum(sock_);
  //}
  clear();
  sock_ = 0;
}
    
bool HttpEndPoint::parseHttpMsg(const char* msg, int msglen)
{
  return handleRequest(msg, msglen);
}

void HttpEndPoint::setHttpEndPoint(int sock)
{
  sock_= sock;
}

void HttpEndPoint::clear()
{
  req_.clear();
  resp_.clear();
}

bool HttpEndPoint::exeNoCgi()
{
  //打开文件
  std::string path = req_.getPath();
  int fd = open(path.c_str(), O_RDONLY);
  if(fd < 0)
  {
    LOG_ERROR("FFL[%s, %s, %u] open file fail path:%s", LOG_FFL, path.c_str());
    return false;
  }

  size_t filesize = req_.getFileSize();
  //Logger::info("---exeNoCgi sock:%d, path:%s, filesize:%u", sock_, path.c_str(), filesize);
  sendfile(sock_, fd, nullptr, filesize);
  close(fd);
  return true;
}
   
bool HttpEndPoint::exeCgi()
{
  //cgi，访问可执行程序
  //创建进程，子进程进程替换
  //参数可以使用环境变量和管道方式传递
  
  //使用环境变量来传递方法
  std::string method_env = "METHOD_ENV=";
  method_env += req_.getMethod();
  putenv((char*)method_env.c_str());
  //Get方法使用环境变量传递参数
  //POST方法使用管道传递参数
  
  //fd[0]读打开 fd[1]写打开，读和写是相对于子进程的
  int fd_in[2] = { 0 };
  int fd_out[2] = { 0 };
  pipe(fd_in);//子进程读参数，父进程写参数
  pipe(fd_out);//子进程写参数，父进程读参数

  pid_t fd = fork();
  if(fd == 0)
  {
    //child
    close(fd_in[1]);
    close(fd_out[0]);

    //Get方法参数因为是环境变量传递，不是从文件中读取
    //读取时不会阻塞，需要提前传递参数
    if(req_.isGetMethod())
    {
      //Get方法有参数
      //std::string param_env = "PARAM_ENV=";
      //param_env += req_.getParam();
      //int ret = putenv((char*)param_env.c_str());
      std::string param = req_.getParam();
      setenv("PARAM_ENV", param.c_str(), 1);
      ////Logger::info("-----------rp:%s, ret:%d", getenv("PARAM_ENV"), ret);
      
    }

    //由于替换进程中不知道管道的文件描述符
    //进行重定向
    dup2(fd_in[0], 0);//重定向到标准输入，读参数
    dup2(fd_out[1], 1);//重定向到标准输出，写结果

    std::string path = req_.getPath();
    execl(path.c_str(), path.c_str(), nullptr);

    exit(0);
  }
  else if (fd > 0)
  {
    //father
    close(fd_in[0]);
    close(fd_out[1]);
    
    if(req_.isPostMethod())
    {
      //post方法
      //将正文写入管道
      std::string body = req_.getBody();
      //Logger::info("FFL[%s, %s, %d] body:%s", LOG_FFL, body.c_str());
      for(size_t i = 0; i < body.size(); ++i)
      {
        write(fd_in[1], &body[i], 1);
      }
    }

    //usleep(100);
    //从管道中读取结果
    ssize_t s = 0;
    char c = 0;
    ssize_t n = 0;
    std::string content;
    do 
    {
      //Logger::info("@@@@@@@@@@@@, s:%d, c:%c, sock:%d, n:%d, threadid:%u", s, c, sock_, n, pthread_self());
      s = read(fd_out[0], &c, 1);
      content += c; 
      if(s > 0)
      {
        n = send(sock_, &c, 1, 0);

      }

    }while(s > 0);

    //Logger::info("^^^^^^^^^");
    //Logger::info("FFL[%s, %s, %d], con:%s", LOG_FFL, content.c_str());
    waitpid(fd, nullptr, 0);
    close(fd_out[0]);
    close(fd_in[1]);
  }
  else
  {
    //创建出错
    LOG_ERROR("FFL[%s, %s, %d], fd:%d", LOG_FFL, fd);
    close(fd_in[0]);
    close(fd_in[1]);
    close(fd_out[0]);
    close(fd_out[1]);
    return false;
  }

  return true;
}

//处理请求
bool HttpEndPoint::handleRequest(const char* msg, int msglen)
{
  //处理消息，获得请求行 报头 空行 正文
  return req_.parseReqMsg(msg, msglen);
}  


bool HttpEndPoint::makeRespLine(size_t code)
{
  std::string& resp_line = resp_.getRespLine(); 
  resp_line += HTTPVERSION;
  resp_line += ' ';
  resp_line += std::to_string(code);
  resp_line += ' ';
  resp_line += code2Description(code);
  resp_line += "\r\n";

  return true;
}
  
bool HttpEndPoint::makeRespHeader()
{
  //得到请求资源后缀
  std::string suffix = req_.getPathSuffix();
  std::string content_type("Content_Type: ");
  content_type += suffix2Desc(suffix);
  content_type += "\r\n";
  resp_.addRespHeader(content_type);
  
  std::string last_modify("Last-Modified: ");
  
  std::time_t mtime_t = req_.getFileMtime();
  struct tm* gmtm = std::gmtime(&mtime_t);
  char mtime_buff[80] = { 0 };
  std::strftime(mtime_buff, sizeof(mtime_buff), "%a, %d %b %Y %H:%M:%S GMT", gmtm);
  last_modify += mtime_buff;
  last_modify += "\r\n";
  resp_.addRespHeader(last_modify);


  std::string e_tag("ETag: ");
  e_tag += std::to_string(req_.getFileSize());
  e_tag += "\r\n";
  resp_.addRespHeader(e_tag);

  return true;
}

bool HttpEndPoint::sendResponse()
{
  //发送响应行
  std::string resp_line = resp_.getRespLine();
  //Logger::info("----sendResponse respline:%s", resp_line.c_str());
  send(sock_, resp_line.c_str(), resp_line.size(), 0);
  
  const std::vector<std::string> resp_header = resp_.getRespHeader();
  for(const auto& c : resp_header)
  {
    //Logger::info("---sendResponse header:%s", c.c_str());
    send(sock_, c.c_str(), c.size(), 0);
  }

  const std::string& blank = resp_.getRespBlank();
  send(sock_, blank.c_str(), blank.size(), 0);
  //Logger::info("---sendResponse blank:%s", blank.c_str());

  if(req_.getCgi())
  {
    exeCgi();
  }
  else 
  {
    exeNoCgi();
  }
  return true;
}

//制作响应
bool HttpEndPoint::makeResponse()//加入到epoll中
{
  //分析请求，制作响应行，响应报头
  //GET请求+参数 cgi技术 path+参数(url中)
  //GET请求+无参 path
  //POST请求 path+参数(正文)
  
  size_t code = 200;
  //goto下面不可以定义临时变量
  std::string path_tmp;
  if(!req_.isLegalMethod())
  {
    code = 400;
    LOG_WARN("FFL[%s, %s, %u] method unlegal", LOG_FFL);
    goto END;
  }
  if(req_.isGetMethod())
  {
    //Get方法，解析url，参数和请求资源路径
    req_.parseUrl();
  }
  else if(req_.isPostMethod())
  {
    //Post方法，设置请求资源路径
    req_.setUrlToPath();
  }

  //当访问的资源路径为/，默认访问index.txt
  req_.addHomePage();
  
  path_tmp = req_.getPath();
  LOG_INFO("FFL[%s, %s, %u] path:%s", LOG_FFL, path_tmp.c_str());
  //如果访问的资源是一个目录
  struct stat st;
  if(stat(path_tmp.c_str(), &st) < 0)
  {
    LOG_ERROR("FFL[%s, %s, %u] 文件不存在 path:%s", LOG_FFL, path_tmp.c_str()); 
    code = 400;
    goto END;
  }
  if(S_ISDIR(st.st_mode))
  {
    //是目录
    path_tmp += '/';
    req_.setPath(path_tmp);
    req_.addHomePage();
  }
  else 
  {
    if(st.st_mode & S_IXUSR || 
        st.st_mode & S_IXOTH || 
        st.st_mode & S_IXGRP)
    {
      //拥有者，组，其用户的可执行文件
      req_.setCgi();
    }
    else 
    {
      //普通网页直接返回
    }
  }
  
  //设置发送普通网页大小
  if(!req_.getCgi())
  {
    req_.setFileSize(st.st_size);
    req_.setFileMtime((size_t)st.st_mtim.tv_sec); 
    //Logger::info("-----filesize:%d", st.st_size);
  }

END:
  makeRespLine(code);
  makeRespHeader();
  return true;
}

HttpRequest::HttpRequest()
  :req_blank_("\r\n")
  ,path_(WEBROOT)
  ,iscgi_(false)
  ,filesize_(0)
  ,content_length_(0)
{}

void HttpRequest::clear()
{
  req_line_.clear();
  req_header_.clear();
  req_blank_ = "\r\n";
  req_body_.clear();
  method_.clear();
  url_.clear();
  version_.clear();
  path_.clear();
  param_.clear();
  iscgi_ = false;
  filesize_ = 0;
  header_map_.clear();
  content_length_ = 0;
}

bool HttpRequest::isLegalMethod()
{
  //Logger::info("----method:%s", method_.c_str());
  //strcasecmp忽略大小写比较
  return strcasecmp(method_.c_str(), "GET") == 0 || strcasecmp(method_.c_str(), "POST") == 0;
}
    
bool HttpRequest::isGetMethod()
{
  return strcasecmp(method_.c_str(), "GET") == 0;
}
    
bool HttpRequest::isPostMethod()
{
  return strcasecmp(method_.c_str(), "POST") == 0;
}

std::string HttpRequest::getPath()
{
  return path_;
}
    
void HttpRequest::setPath(const std::string& path)
{
  path_ = path;
}
    
bool HttpRequest::getCgi()
{
  return iscgi_;
}

void HttpRequest::setCgi()
{
  iscgi_ = true;
}
    
void HttpRequest::setFileSize(size_t size)
{
  filesize_ =  size;
}

void HttpRequest::setFileMtime(std::time_t mtime)
{
  file_mtime_ = mtime;
}

bool HttpRequest::parseUrl()
{
  //URL参数和资源路径?分割
  size_t pos = url_.find('?');
  if(pos == std::string::npos)
  {
    path_ += url_;
  }
  else 
  {
    //get方法且有参数
    path_ += url_.substr(0, pos);
    param_ = url_.substr(pos + 1);
    iscgi_ = true;
  }
  return true;
  //Logger::info("ppppppp url:%s, param:%s", url_.c_str(), param_.c_str());
}
    
std::string HttpRequest::getPathSuffix()
{
  size_t pos = path_.rfind('.');
  if(pos != std::string::npos)
  {
    return path_.substr(pos + 1);
  }
  return std::string();
}
    
size_t HttpRequest::getFileSize()
{
  return filesize_;
}

std::time_t HttpRequest::getFileMtime()
{
  return file_mtime_;
}

const std::string& HttpRequest::getMethod()
{
  return method_;
}
    
const std::string& HttpRequest::getParam()
{
  return param_;
}
    
const std::string& HttpRequest::getBody()
{
  return req_body_;
}
    
bool HttpRequest::setUrlToPath()
{
  path_ += url_;
}
    
void HttpRequest::addHomePage()
{
  if(path_[path_.size() - 1] == '/')
  {
    path_ += HOMEPAGE;
  }
}

HttpRequest::~HttpRequest()
{
  
}

bool HttpRequest::parseReqMsg(const char* msg, int msglen)
{
  std::string msg_str(msg);
  size_t beginpos = 0;
  size_t findpos = 0;
  size_t pos = msg_str.find('\r');
  if(pos != std::string::npos)
  {
    // \r
    findpos = pos + 1;
    if(pos + 1 < msg_str.size())
    {
      // \r\n
      if(msg_str[pos + 1] == '\n')
      {
        findpos++;
      }
    }
    req_line_ = msg_str.substr(beginpos, pos - beginpos);
  }
  else 
  {
    // \n
    pos = msg_str.find('\n');
    if(pos != std::string::npos)
    {
      findpos = pos + 1;
      req_line_ = msg_str.substr(beginpos, pos - beginpos);
    }
  }
  LOG_INFO("FFL[%s, %s, %d], line:%s", LOG_FFL, req_line_.c_str());

  //解析请求行的，请求方法，url，http版本
  if(!req_line_.empty())
  {
    size_t line_beginpos = 0;
    size_t line_pos = req_line_.find(' ');
    if(line_pos != std::string::npos)
    {
      method_ = req_line_.substr(line_beginpos, line_pos - line_beginpos); 
      line_beginpos = line_pos + 1;
    }
    line_pos = req_line_.find(' ', line_beginpos);
    if(line_pos != std::string::npos)
    {
      url_ = req_line_.substr(line_beginpos, line_pos - line_beginpos);
      line_beginpos = line_pos + 1;
    }

    version_ = req_line_.substr(line_beginpos);

    LOG_INFO("FFL[%s, %s, %d] method:%s, url:%s， version:%s", LOG_FFL, method_.c_str(), url_.c_str(), version_.c_str());
  }

  std::string line;
  do 
  {
    line.clear();

    beginpos = findpos;
    pos = msg_str.find('\r', findpos);
    if(pos != std::string::npos)
    {
      // \r
      findpos = pos + 1;
      if(pos + 1 < msg_str.size())
      {
        // \r\n
        if(msg_str[pos + 1] == '\n')
        {
          findpos++;
        }
      }
      line = msg_str.substr(beginpos, pos - beginpos);
    }
    else 
    {
      // \n
      pos = msg_str.find('\n', findpos);
      if(pos != std::string::npos)
      {
        findpos = pos + 1;
        line = msg_str.substr(beginpos, pos - beginpos);
      }
    }
    if(!line.empty())
      req_header_.push_back(line);

    LOG_INFO("FFL[%s, %s, %d], line:%s", LOG_FFL, line.c_str());
  }while(!line.empty());

  for(const auto& str : req_header_)
  {
    size_t str_pos = str.find(':');
    if(str_pos != std::string::npos)
    {
      std::string k_str = str.substr(0, str_pos);
      std::string v_str = str.substr(str_pos + 2);
      header_map_[k_str] = v_str;
      if(strcmp(k_str.c_str(), "Content-Length") == 0)
      {
        content_length_ = atoi(v_str.c_str());
      }
      LOG_INFO("FFL[%s, %s, %d] k_str:%s, v_str:%s, contentl:%u", LOG_FFL, k_str.c_str(), v_str.c_str(), content_length_);
    }

  }

  beginpos = findpos;
  req_body_ = msg_str.substr(beginpos, content_length_);

  LOG_INFO("FFL[%s, %s, %d], line:%s", LOG_FFL, req_body_.c_str());
  return true;
}
    
HttpResponse::HttpResponse()
  :blank_("\r\n")
{}

void HttpResponse::clear()
{
  resp_line_.clear();
  resp_body_.clear();
  blank_ = "\r\n";
  resp_header_.clear();
}

std::string& HttpResponse::getRespLine()
{
  return resp_line_;
}
    
const std::vector<std::string>& HttpResponse::getRespHeader()
{
  return resp_header_;
}
    
void HttpResponse::addRespHeader(std::string& str)
{
  resp_header_.push_back(str);
}

const std::string& HttpResponse::getRespBlank()
{
  return blank_;
}

